package org.saxonextensions.eml.dispatcher;

import org.saxonextensions.eml.emitter.MailSessionFactory;
import org.saxonextensions.eml.emitter.MimeMessageWrapper;

import javax.mail.*;
import javax.mail.internet.InternetAddress;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Simple EML Dispatcher.
 *
 * @author Ivan Latysh <ivan@yourmail.com>
 * @version 0.1
 * @since 5-May-2008 4:33:05 PM
 */
public abstract class AbstractSimpleEMLDispatcher implements EMLDispatcher {

  /** Logger */
  Logger logger = Logger.getLogger(this.getClass().getName());

  /**
   * Return mail session
   *
   * @return session
   */
  public abstract Session getSession() throws Exception;

  /**
   * Dispatch all messages
   *
   * @param eml file list
   * @param ignoreErrors when set, exceptions are logged but not interrupting
   * @throws Exception when unable to process a file
   */
  public void dispatch(File[] eml, boolean ignoreErrors) throws Exception {
    InputStream in = null;
    for(File file: eml) {
      try {
        // read the message
        in = new FileInputStream(file);
        // dispatch it
        dispatch(in);
      } catch(Exception ex) {
        if (!ignoreErrors) throw ex;
        logger.warning("Unable to process a message {"+(null!=file ?file.getAbsolutePath() :"null")+"}");
      } finally {
        if (null!=in) in.close();
      }
    }
  }

  /**
   * Process an input stream as serialized message
   *
   * @param in input stream with serialized message
   * @throws Exception when unable to process a message
   */
  public void dispatch(InputStream in) throws Exception {
    Session session = MailSessionFactory.getSession();
    dispatch(new MimeMessageWrapper(session, in));
  }

  /**
   * Process the message
   *
   * @param msg message to dispatch
   * @throws Exception when unable to process it
   */
  public void dispatch(MimeMessageWrapper msg) throws Exception {
    long sendTime;
    final long sendStart = System.currentTimeMillis();
    Transport tr = null;
    Session session = null;

    try {
      session = msg.getSession();
      tr = session.getTransport("smtp");

      int tryCount = 0;

      try {
        tr.connect();
      } catch (MessagingException e) {
        logger.log(Level.SEVERE, "Unable to connect to SMTP server. Will try send the message {"+msg+"} anyway.", e);
      }

      tryCount = 0;
      boolean sent = false;
      while (!sent) {
        try {
          // send message
          tr.sendMessage(msg.getMessage(), msg.getAllRecipients());
          // set send flag
          sent = true;
        } catch (IllegalStateException e) {
          // the connection can be lost. Try to reconnect.
          logger.log(Level.SEVERE, "Error while sending the message {"+msg+"}, will try to reconnect.", e);
          if (tryCount <3 && !tr.isConnected()) {
            synchronized(this) {
              try {
                wait(10000);
              } catch (InterruptedException e1) {
                logger.log(Level.SEVERE, "Interrupted", e1);
              }
            }
            tr.connect();
            tryCount++;
          } else {
            logger.log(Level.SEVERE, "Unable to send the message {"+msg+"}, tried {"+tryCount+"} times.", e);
            throw e;
          }
        }
      }
      sendTime = System.currentTimeMillis()-sendStart;
      // log message sent email
      if (logger.isLoggable(Level.FINEST)) {
        logger.info("Message to "+Arrays.toString(msg.getAllRecipients())+" send successfully. In "+sendTime+"ms.");
      }
    } catch (Exception e) {
      String rcp = "";
      try {
        if (null!=msg && null!=msg.getAllRecipients()) {
          for (Address recipient : msg.getAllRecipients()) {
            rcp += "[" + ((InternetAddress)recipient).getAddress() + "]";
          }
        }
      } catch (MessagingException e1) {
        logger.log(Level.SEVERE, "Unable retrieve recipients from the message for logging.", e1);
      }
      logger.log(Level.SEVERE, "Unable to sent the message to {"+rcp+"}, dumping eml.", e);
      dumpMessage(msg.getMessage());
    } finally {
      try {
        tr.close();
      } catch (MessagingException e) {
        logger.log(Level.SEVERE, "Unable to close Transport", e);
      }
    }

  }

  /**
   * Dump given message.
   * TODO move to a separate tools class
   *
   * @param message message to dump to file
   */
  public static void dumpMessage(Message message) {
    final String filename = System.currentTimeMillis()+".eml";
    if (null!=message) {
      try {
        final File file = new File("eml-dump",filename);
        if (!file.getParentFile().exists()) file.getParentFile().mkdirs();
        file.createNewFile();
        message.writeTo(new FileOutputStream(file));
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }

}
